package com.ht.graphmodel;

import com.ht.aems.ieeeformat.IEEEDataIsland;
import com.ht.aems.ieeeformat.BusData;
import com.ht.aems.ieeeformat.IslandTopoUtil;
import com.mxgraph.model.mxCell;
import com.mxgraph.model.mxGraphModel;
import com.mxgraph.model.mxIGraphModel;
import com.mxgraph.util.mxUtils;
import com.mxgraph.view.mxGraph;
import com.mxgraph.io.mxCodec;

import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.io.IOException;

import org.w3c.dom.Element;
import org.w3c.dom.Document;

/**
 * Created by IntelliJ IDEA.
 *
 * @author Wang Jiaxiang
 *         Date: 2011-1-5
 */
public class IEEEIslandUtil {

    private static Document doc = mxUtils.createDocument();
    private static Map<Object, Object[]> line2Nodes = new HashMap<Object, Object[]>(188);
    private static Map<BusData, Map<BusData, String>> connectMap1 = new HashMap<BusData, Map<BusData, String>>();
    private static Map<BusData, Map<BusData, String>> connectMap2 = new HashMap<BusData, Map<BusData, String>>();
    private static Map<BusData, Object> dataPassed = new HashMap<BusData, Object>();
    //todo: not good
    private static Map<String, double[]> nodeAddr = IeeeNodeModel.getNodeAddr("d:\\118.mxe");
    static mxGraph graph;
    static Object parent;

    public static void mxeParser(IEEEDataIsland island, String outPutFile) {
        mxGraphModel model = mxeParser(island);
        mxCodec codec = new mxCodec();
        String xml = mxUtils.getXml(codec.encode(model));
        try {
            mxUtils.writeFile(xml, outPutFile);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static mxGraphModel mxeParser(IEEEDataIsland island) {
        Map<BusData, List<BusData>> bus2Buses = IslandTopoUtil.getBus2Buses(island);
        BusData rootBus = island.getBus(island.getSlackBusNum());
        Element rootEle = getElement(rootBus);
        List<BusData> busData = bus2Buses.get(rootBus);
        graph = new mxGraph();
        parent = graph.getDefaultParent();
        connectMap1.clear();
        connectMap2.clear();
        if (busData.size() > 0) {
            for (BusData bus : busData) {
                if (connectMap1.containsKey(bus)) {
                    if (connectMap1.get(bus).containsKey(rootBus))
                        continue;
                }
                if (connectMap2.containsKey(bus)) {
                    if (connectMap2.get(bus).containsKey(rootBus))
                        continue;
                }
                Element ele = getElement(bus);
                Object v1 = graph.insertVertex(parent, null, rootEle, nodeAddr.get(rootEle.getAttribute("id"))[0], nodeAddr.get(rootEle.getAttribute("id"))[1], 100,
                        100);
                mxCell cell = (mxCell) v1;
                paintMark(cell);
                Object line;
                Object[] nodes;
                if (dataPassed.containsKey(bus)) {
                    line = graph.insertEdge(parent, null, "", v1, dataPassed.get(bus));
                    nodes = new Object[2];
                    nodes[0] = v1;
                    nodes[1] = dataPassed.get(bus);
                    line2Nodes.put(line, nodes);
                    connect2Element(bus, rootBus);
                    continue;
                }
                Object v2 = graph.insertVertex(parent, null, ele, nodeAddr.get(ele.getAttribute("id"))[0], nodeAddr.get(ele.getAttribute("id"))[1],
                        100, 100);
                paintMark((mxCell) v2);
                connect2Element(rootBus, bus);
                line = graph.insertEdge(parent, null, "", v1, v2);
                nodes = new Object[2];
                nodes[0] = v1;
                nodes[1] = v2;
                line2Nodes.put(line, nodes);
                dataPassed.put(rootBus, v1);
                dataPassed.put(bus, v2);
                searchNext(bus2Buses, bus, v2);
            }
        }
        graph.getModel().endUpdate();
        return (mxGraphModel) graph.getModel();
    }

    private static void searchNext(Map<BusData, List<BusData>> bus2Buses, BusData bus, Object v2) {
        List<BusData> busData = bus2Buses.get(bus);
        if (busData.size() > 0) {
            for (BusData b : busData) {
                if (connectMap1.containsKey(b)) {
                    if (connectMap1.get(b).containsKey(bus))
                        continue;
                }
                if (connectMap2.containsKey(b)) {
                    if (connectMap2.get(b).containsKey(bus))
                        continue;
                }
                Object line;
                if (dataPassed.containsKey(b)) {
                    line = graph.insertEdge(parent, null, "", v2, dataPassed.get(b));
                    Object[] nodes = new Object[2];
                    nodes[0] = v2;
                    nodes[1] = dataPassed.get(b);
                    line2Nodes.put(line, nodes);
                    connect2Element(b, bus);
                    continue;
                }
                Element ele = getElement(b);
                Object newObj = graph.insertVertex(parent, null, ele, nodeAddr.get(ele.getAttribute("id"))[0], nodeAddr.get(ele.getAttribute("id"))[1],
                        100, 100);
                paintMark((mxCell) newObj);
                connect2Element(bus, b);
                dataPassed.put(b, newObj);
                line = graph.insertEdge(parent, null, "", v2, newObj);
                Object[] nodes = new Object[2];
                nodes[0] = v2;
                nodes[1] = newObj;
                line2Nodes.put(line, nodes);
                searchNext(bus2Buses, b, newObj);
            }
        }

    }

    private static Element getElement(BusData bus) {
        Element element = doc.createElement("BusData");
        element.setAttribute("id", String.valueOf(bus.getBusNumber()));
        Field[] fields = BusData.class.getFields();
        String attName;
        Method meth;
        Object value;
        String attValue;
        try {
            for (Field field : fields) {
                attValue = field.get(field).toString();
                attName = "get" + attValue.substring(0, 1).toUpperCase() + attValue.substring(1);
                meth = BusData.class.getMethod(attName);
                value = meth.invoke(bus);
                element.setAttribute(attValue, value.toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return element;
    }

    private static void paintMark(mxCell cell) {
        cell.setStyle("ellipse;shape=ellipse");
    }

    private static void connect2Element(BusData rootBus, BusData bus) {
        if (!connectMap1.containsKey(rootBus)) {
            Map<BusData, String> map = new HashMap<BusData, String>();
            map.put(bus, "s");
            connectMap1.put(rootBus, map);
        } else {
            connectMap1.get(rootBus).put(bus, "s");
        }
        if (!connectMap2.containsKey(bus)) {
            Map<BusData, String> map = new HashMap<BusData, String>();
            map.put(rootBus, "s");
            connectMap2.put(bus, map);
        } else {
            connectMap2.get(bus).put(rootBus, "s");
        }
    }

    public static Map<Object, Object[]> getLine2Nodes() {
        return line2Nodes;
    }
}
